﻿using System.Linq.Expressions;
using KL.Standard.Domain.Repositories;
using KL.Standard.Model.Attribute;
using KL.Standard.Model.Infrastructure;
using MongoDB.Driver;

namespace KL.Standard.Mongo.Repositories
{
     public abstract class BMongoRepository<T> : IDocRepository<T> where T : class
    {
        public  KLMongoClient Client { get; set; }

        

        protected string GetCollectionName(string defaultName=null)
        {
            var clnName = defaultName;
            if (string.IsNullOrEmpty(clnName))
            {
                var attribute = this.GetType().GetCustomAttributes(typeof(MongoAttribute), false).FirstOrDefault() as MongoAttribute;
                if (attribute != null)
                {
                    clnName = attribute.collectionName;
                }
            }

            if (!string.IsNullOrEmpty(clnName))
                return clnName;
            else
            {
                throw new Exception("Can not get collection name!");
            }
        }
        

        #region 增加

        /// <summary>
        /// 插入对象
        /// </summary>
        /// <param name="tableName">集合名字</param>
        /// <param name="t">插入的对象</param>
        protected void Insert(string tableName, T t)
        {
            var collection = Client.Database.GetCollection<T>(tableName);
            collection.InsertOne(t);
        }

        /// <summary>
        /// 批量插入
        /// </summary>
        /// <param name="tableName">集合名称</param>
        /// <param name="ts">要插入的对象集合</param>
        protected void InsertBath(string tableName, IEnumerable<T> ts)
        {
            var collection = Client.Database.GetCollection<T>(tableName);
            collection.InsertManyAsync(ts);
        }

        #endregion

        #region 删除

        /// <summary>
        /// 按检索条件删除
        /// </summary>
        /// <param name="tableName">集合名称</param>
        /// <param name="filter">过滤</param>
        /// <returns></returns>
        protected async Task<long> Delete(String tableName, MongoDB.Driver.FilterDefinition<T> filter)
        {
            var collection = Client.Database.GetCollection<T>(tableName);
            var result = await collection.DeleteManyAsync(filter);
            return result.DeletedCount;
        }

        /// <summary>
        /// 按条件表达式删除
        /// </summary>
        /// <param name="tableName">集合名称</param>
        /// <param name="predicate">条件表达式</param>
        /// <returns></returns>
        protected async Task<long> Delete(String tableName, Expression<Func<T, Boolean>> predicate)
        {
            var collection = Client.Database.GetCollection<T>(tableName);
            var result = await collection.DeleteManyAsync(predicate);
            return result.DeletedCount;
        }

        #endregion

        #region 修改

        /// <summary>
        /// 修改文档
        /// </summary>
        /// <param name="tableName">集合名称</param>
        /// <param name="filter">修改条件</param>
        /// <param name="update">修改结果</param>
        /// <param name="upsert">是否插入新文档（filter条件满足就更新，否则插入新文档）</param>
        /// <returns></returns>
        protected Int64 Update(String tableName, Expression<Func<T, Boolean>> filter, UpdateDefinition<T> update,
            Boolean upsert = false)
        {
            var collection = Client.Database.GetCollection<T>(tableName);
            var result = collection.UpdateMany(filter, update, new UpdateOptions {IsUpsert = upsert});
            return result.ModifiedCount;
        }

        /// <summary>
        /// 用新对象替换新文档-更新批量
        /// </summary>
        /// <param name="tableName">集合名称</param>
        /// <param name="filter">修改条件</param>
        /// <param name="t">新对象</param>
        /// <param name="upsert">是否插入新文档（filter条件满足就更新，否则true插入新文档）</param>
        /// <returns>修改影响文档数</returns>
        // public Int64 UpdateT(String tableName, FilterDefinition<T> filter, T t, Boolean upsert = false)
        // {
        //     var collection = Client.Database.GetCollection<T>(tableName);
        //     BsonDocument document = t.ToBsonDocument<T>();
        //     document.Remove("_id");
        //     UpdateDocument update = new UpdateDocument("$set", document);
        //     var result = coll.UpdateMany(filter, update, new UpdateOptions { IsUpsert = upsert });
        //     return result.ModifiedCount;
        // }
        /// <summary>
        /// 用新对象替换新文档-更新单个
        /// </summary>
        /// <param name="collName">集合名称</param>
        /// <param name="filter">修改条件</param>
        /// <param name="t">新对象</param>
        /// <param name="upsert">是否插入新文档（filter条件满足就更新，否则true插入新文档）</param>
        /// <returns>修改影响文档数</returns>
        // public Int64 UpdateOne(String collName, FilterDefinition<T> filter, T t, Boolean upsert = false)
        // {
        //     var coll = GetColletion(collName);
        //     BsonDocument document = t.ToBsonDocument<T>();
        //     document.Remove("_id");
        //     UpdateDocument update = new UpdateDocument("$set", document);
        //     var result = coll.UpdateOne(filter, update, new UpdateOptions { IsUpsert = upsert });
        //     return result.ModifiedCount;
        // }

        #endregion

        #region 查询

        /// <summary>
        /// 查询，复杂查询直接用Linq处理
        /// </summary>
        /// <param name="tableName">集合名称</param>
        /// <returns>要查询的对象</returns>
        protected IQueryable<T> GetQueryable(String tableName)
        {
            var collection = Client.Database.GetCollection<T>(tableName);
            return collection.AsQueryable<T>();
        }

        /// <summary>
        /// 根据条件表达式返回可查询的记录源
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="query">查询条件</param>
        /// <param name="sortPropertyName">排序表达式(可以为null表示不排序)</param>
        /// <param name="isDescending">如果为true则为降序，否则为升序</param>
        /// <returns></returns>
        protected IFindFluent<T, T> GetQueryable(String tableName, FilterDefinition<T> query, string sortPropertyName,
            bool isDescending = true)
        {
            IMongoCollection<T> collection = Client.Database.GetCollection<T>(tableName);
            IFindFluent<T, T> queryable = collection.Find(query);
            if (!string.IsNullOrEmpty(sortPropertyName))
            {
                var sort = isDescending
                    ? Builders<T>.Sort.Descending(sortPropertyName)
                    : Builders<T>.Sort.Ascending(sortPropertyName);
                return queryable.Sort(sort);
            }

            return queryable;
        }

        /// <summary>
        /// 根据条件表达式返回可查询的记录源
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="match">查询条件</param>
        /// <param name="orderByProperty">排序表达式</param>
        /// <param name="isDescending">如果为true则为降序，否则为升序</param>
        /// <returns></returns>
        protected IQueryable<T> GetQueryable<TKey>(String tableName, Expression<Func<T, bool>> match,
            Expression<Func<T, TKey>> orderByProperty, bool isDescending = true)
        {
            IMongoCollection<T> collection = Client.Database.GetCollection<T>(tableName);
            IQueryable<T> query = collection.AsQueryable();

            if (match != null)
            {
                query = query.Where(match);
            }

            if (orderByProperty != null)
            {
                query = isDescending ? query.OrderByDescending(orderByProperty) : query.OrderBy(orderByProperty);
            }
            else
            {
                // query = query.OrderBy(sortPropertyName, isDescending);
            }

            return query;
        }

        /// <summary>
        /// 根据条件查询数据库,并返回对象集合
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="match">条件表达式</param>
        /// <param name="sortPropertyName">排序字段</param>
        /// <param name="isDescending">如果为true则为降序，否则为升序</param>
        /// <returns></returns>
        protected IList<T> Find(String tableName, Expression<Func<T, bool>> match, string sortPropertyName,
            bool isDescending = true)
        {
            return GetQueryable(tableName, match, sortPropertyName, isDescending).ToList();
        }

        /// <summary>
        /// 根据条件查询数据库,并返回对象集合
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="query">条件表达式</param>
        /// <param name="sortPropertyName">排序字段</param>
        /// <param name="isDescending">如果为true则为降序，否则为升序</param>
        /// <returns></returns>
        protected IList<T> Find(String tableName, FilterDefinition<T> query, string sortPropertyName,
            bool isDescending = true)
        {
            return GetQueryable(tableName, query, sortPropertyName, isDescending).ToList();
        }
        
        /// <summary>
        /// 根据条件查询数据库,并返回对象集合
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="query">条件表达式</param>
        /// <param name="sortPropertyName">排序字段</param>
        /// <param name="isDescending">如果为true则为降序，否则为升序</param>
        /// <returns></returns>
        protected T FindSingle(String tableName, FilterDefinition<T> query, string sortPropertyName,
            bool isDescending = true)
        {
            return GetQueryable(tableName, query, sortPropertyName, isDescending).FirstOrDefault();
        }


        /// <summary>
        /// 根据条件查询数据库,并返回对象集合
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="match">条件表达式</param>
        /// <param name="orderByProperty">排序表达式</param>
        /// <param name="isDescending">如果为true则为降序，否则为升序</param>
        /// <returns></returns>
        protected IList<T> Find<TKey>(String tableName, Expression<Func<T, bool>> match,
            Expression<Func<T, TKey>> orderByProperty, bool isDescending = true)
        {
            return GetQueryable<TKey>(tableName, match, orderByProperty, isDescending).ToList();
        }

        /// <summary>
        /// 根据条件查询数据库,如果存在返回第一个对象
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="filter">条件表达式</param>
        /// <returns>存在则返回指定的第一个对象,否则返回默认值</returns>
        protected T FindSingle(String tableName, FilterDefinition<T> filter)
        {
            var coll = Client.Database.GetCollection<T>(tableName);
            return coll.Find(filter).FirstOrDefault();
        }
        
        
        /// <summary>
        /// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="query">条件表达式</param>
        /// <param name="info">分页实体</param>
        /// <returns>指定对象的集合</returns>
        public PageResult<T> FindWithPager(String tableName, FilterDefinition<T> query, PageSearchModel info)
        {
            var info2 =info.ToPageResult<T>();
            return FindWithPager(tableName, query, info2, info.Sort, true);
        } 
        

        /// <summary>
        /// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
        /// </summary>
        /// <param name="tableName">集合</param>
        /// <param name="query">条件表达式</param>
        /// <param name="info">分页实体</param>
        /// <returns>指定对象的集合</returns>
        public PageResult<T> FindWithPager(String tableName, FilterDefinition<T> query, PageResult<T> info,
            string orderByProperty, bool isDescending = true)
        {
            int pageindex = info.pageIndex;
            int pageSize = info.pageSize;
            int excludedRows = (pageindex - 1) * pageSize;
            var find = GetQueryable(tableName, query, orderByProperty, isDescending);
            info.totalCount = (int) find.CountDocuments();
            info.rows = find.Skip(excludedRows).Limit(pageSize).ToList();
            return info;
        } 
        

        #endregion
    }

}